export default (function () {
  /**
   * 把类数组对象转换为数组。
   */
  var toArray = Array.from || function (arg) {
      return Array.prototype.slice.call(arg);
    };

  /**
   * 判断参数是否为节点对象。
   * @param {?} arg
   * @return {boolean}
   */
  function isNode(arg) {
    return arg instanceof Node;
  }

  /**
   * 判断参数是否为script节点对象。
   * @param {?} arg
   * @return {boolean}
   */
  function isScript(arg) {
    return isNode(arg) && arg.nodeName.toLowerCase() === 'script';
  }

  /**
   * 复制script节点对象，复制以后script标签的内容可以运行。
   * @param {object} script script节点对象
   * @return {object} 返回新的script节点对象
   */
  function copyScript(script) {
    var newScript = document.createElement('script');

    // 复制属性
    toArray(script.attributes).forEach(function (item) {
      newScript.setAttribute(item.nodeName, item.nodeValue);
    });

    newScript.innerHTML = script.innerHTML;

    return newScript;
  }

  /**
   * 替代script节点对象，替代以后script标签的内容可以运行。
   * @param {object} node 节点对象
   * @return {object} 返回script被替代的节点对象（script节点对象被替代成可以运行的）
   */
  function replaceAllScript(node) {
    Array.prototype.forEach.call(node.childNodes, function (item) {
      // 如果是script节点对象，则替代为可运行的script节点对象
      if(isScript(item)) {
        item.parentNode.replaceChild(copyScript(item), item);
        return;
      }
      // 递归
      if(item.childNodes.length > 0){
        replaceAllScript(item);
      }
    });

    return node;
  }

  /**
   * 字符串转成文档片段节点对象。
   * @param {String} str 字符串，可以包含script标签，转换完插入文档以后可以运行。
   * @return {Object} 返回文档片段节点对象
   */
  function strToNode(str) {
    var docFrag = document.createDocumentFragment(),
      div = document.createElement('div');

    div.innerHTML = str;

    toArray(replaceAllScript(div).childNodes).forEach(function (item) {
      docFrag.appendChild(item);
    });

    return docFrag;
  }

  /**
   * 用于扩展appendChild方法。可以传入html字符串和节点对象，可以传多个参数。
   * 如果html字符串中有script标签，插入以后可以正常运行。
   * 注意，所有参数是一起插入指定节点的，而不是一个一个插入。
   */
  Node.prototype.appendChildPlus = function () {
    var docFrag = document.createDocumentFragment();

    toArray(arguments).forEach(function (arg) {
      docFrag.appendChild(isNode(arg) ? arg : strToNode(String(arg)));
    });

    this.appendChild(docFrag);
  };
})();
